// @ts-nocheck
// Modify n value to change the difficulty of the benchmark
// suggesting using input = 25000000;
const input: number = 25000000;

declare interface ArkTools {
  timeInUs(arg:any):number
}

type AminoAcid = {prob: number; sym: number;};

const IM = 139968;
const IA = 3877;
const IC = 29573;
let seed = 42;

let n: number = input;

const bufferSize: number = 256 * 1024;
const width: number = 60;

const aluString: string =
  "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" +
  "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" +
  "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" +
  "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" +
  "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" +
  "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" +
  "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
let alu = new Array(aluString.length);
for (let i = 0; i < aluString.length; i++) {
	alu[i] = aluString[i].charCodeAt(0);
}
alu.pop();

const iub: AminoAcid[] = [
  { prob: 0.27, sym: 97}, // "a"),
  { prob: 0.12, sym: 99}, // "c"),
  { prob: 0.12, sym: 103}, // "g"),
  { prob: 0.27, sym: 116}, // "t"),
  { prob: 0.02, sym: 66}, // "B"),
  { prob: 0.02, sym: 68}, // "D"),
  { prob: 0.02, sym: 72}, // "H"),
  { prob: 0.02, sym: 75}, // "K"),
  { prob: 0.02, sym: 77}, // "M"),
  { prob: 0.02, sym: 78}, // "N"),
  { prob: 0.02, sym: 82}, // "R"),
  { prob: 0.02, sym: 83}, // "S"),
  { prob: 0.02, sym: 86}, // "V"),
  { prob: 0.02, sym: 87}, // "W"),
  { prob: 0.02, sym: 89}, // "Y"),
];

const homosapiens: AminoAcid[] = [
  { prob: 0.3029549426680, sym: 97}, // "a"),
  { prob: 0.1979883004921, sym:  99}, // "c"),
  { prob: 0.1975473066391, sym:  103}, // "g"),
  { prob: 0.3015094502008, sym:  116}, // "t"),
];

function repeatFasta(gene: number[], n: number) {
  const gene2 = [...gene, ...gene];
  let buffer = new Array(bufferSize).fill(10);
  let pos = 0;
  let rpos = 0;
  let cnt = n;
  let lwidth = width;

  while (cnt > 0) {
    if (pos + lwidth > buffer.length) {
        pos = 0;
    }
    if (rpos + lwidth > gene.length) {
        rpos = rpos % gene.length;
    }
    if (cnt < lwidth) {
        lwidth = cnt;
    }
	for (let i = 0; i < lwidth; i++) {
		buffer[pos + i] = gene2[rpos + i];
	}
    buffer[pos + lwidth] = 10;
    pos += lwidth + 1;
    rpos += lwidth;
    cnt -= lwidth;
  }
  if (pos > 0 && pos < buffer.length) {
    buffer[pos] = 10;
  } else if (pos === buffer.length) {
    buffer[0] = 10;
  }
}

function search(rnd: number, arr: AminoAcid[]): number {
    let low = 0;
    let high = arr.length - 1;

    while (low <= high) {
        const mid = low + Math.floor((high - low) / 2);
        if (arr[mid].prob >= rnd) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }

    return arr[high + 1].sym;
}

function accumulateProbabilities(acid: AminoAcid[]): void {
  for (let i = 1; i < acid.length; i++) {
    acid[i].prob += acid[i-1].prob;
  }
}

function randomFasta(acid: AminoAcid[], n: number) {
  let cnt = n;
  accumulateProbabilities(acid);
  let buffer = new Array(bufferSize).fill(10);
  let pos = 0;
  while (cnt > 0) {
    let m = cnt > width ? width : cnt;
    let f = 1.0 / IM;
    let myrand = seed;
    for (let i = 0; i < m; i++) {
      myrand = (myrand * IA + IC) % IM;
      let r = myrand * f;
      buffer[pos] = search(r, acid);
      pos++;
      if (pos === buffer.length) {
        pos = 0;
      }
    }
    seed = myrand;
    buffer[pos] = 10;
    pos++;
    if (pos === buffer.length) {
      pos = 0;
    }
    cnt -= m;
  }
}

export function runRepeatFasta(){
  let start = ArkTools.timeInUs();
  repeatFasta(alu, 2*n);
  let end = ArkTools.timeInUs();
  print("Numerical Calculation  - repeatFasta" + (end - start) / 1000);
}
runRepeatFasta()
export function runRandomFasta1(){
  let start = ArkTools.timeInUs();
  randomFasta(iub, 3*n);
  let end = ArkTools.timeInUs();
  print("Numerical Calculation  - randomFasta2" + (end - start) / 1000);
}


export function runRandomFasta2(){
  let start = ArkTools.timeInUs();
  randomFasta(homosapiens, 5*n);
  let end = ArkTools.timeInUs();
  print("Numerical Calculation  - randomFasta3" + (end - start) / 1000);
}